package cn.remex.wechat;

import cn.remex.RemexConstants;
import cn.remex.core.RemexApplication;
import cn.remex.core.exception.ServiceCode;
import cn.remex.core.util.Assert;
import cn.remex.core.util.FileHelper;
import cn.remex.core.util.Judgment;
import cn.remex.core.util.MapHelper;
import cn.remex.core.util.XmlHelper;
import cn.remex.web.service.BsRvo;
import cn.remex.web.service.BusinessService;
import cn.remex.wechat.beans.paymch.PayNotify;
import cn.remex.wechat.config.WeChatApp;
import cn.remex.wechat.event.*;
import cn.remex.wechat.utils.Signature;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import java.io.File;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Map;

/**
 * 微信回调地址
 * Created by guoqi on 2016/2/26.
 */
@BusinessService
public class WeChatCoreService {
    /*
     * 验证服务器地址的有效性
     *
     */
    @BusinessService(bodyParamName = "xml", needAuth = false)
    public BsRvo dispatchWeChat(String appid, String agentid, String signature,String msg_signature, String timestamp, String nonce,String echoStr, String echostr, String xml) {
        WeChatApp curWeChatApp = WeChat.getWeChatApp(appid, agentid);
        signature = Judgment.nullOrBlank(signature) ? msg_signature:signature;//企业号和服务号变量名兼容一下
        echostr = Judgment.nullOrBlank(echostr) ? echoStr:echostr;//企业号和服务号变量名兼容一下

        if (!Judgment.nullOrBlank(echostr)) {  //微信服务器验证
            // 说明是一个仅仅用来验证的请求，返回echostr
            String ret_echostr = curWeChatApp.verifyURL(signature, timestamp, nonce, echostr); // TODO 如何自动识别那个app？
            RemexConstants.logger.info("微信->验证请求！");
            return new BsRvo(ServiceCode.SUCCESS, "OK", ret_echostr, RemexConstants.ResponseContentType.text, null);
        }
        if (!Judgment.nullOrBlank(xml)) { // 微信
            String resXml = this.XmlOperation(curWeChatApp, signature, timestamp, nonce, xml);
            return new BsRvo(ServiceCode.SUCCESS, "OK", resXml, RemexConstants.ResponseContentType.text, null);
        }
        //微信错误请求！
        RemexConstants.logger.info("微信->错误请求！");
        return new BsRvo(ServiceCode.SUCCESS, "OK", "请求错误", RemexConstants.ResponseContentType.text, null);
    }
    //微信事件触发
    private String XmlOperation(WeChatApp curWeChatApp, String signature, String timeStamp, String nonce, String reqXml) {
        String resXml = "";//响应的数据
        try {
            //解密处理
            reqXml = curWeChatApp.decryptMsg(signature, timeStamp, nonce, reqXml);
            RemexConstants.logger.info("微信->回调信息！ 为：{}" , reqXml);

            Document document = DocumentHelper.parseText(reqXml);
            String msgType = document.getRootElement().elementText("MsgType");
            String fromUserName = document.getRootElement().elementText("FromUserName");
            String toUserName = document.getRootElement().elementText("ToUserName");
            long createTime = Long.parseLong(document.getRootElement().elementText("CreateTime"));
            String event = document.getRootElement().elementText("Event");
            String eventKey = document.getRootElement().elementText("EventKey");
            String ticket = document.getRootElement().elementText("Ticket");
            Assert.notNullAndEmpty(msgType, ServiceCode.FAIL, "微信回调消息类型为空！");
            this.saveXMLFile(reqXml, msgType, null, fromUserName);

            //定义返回的xml的document，设置ToUserName和FromUserName
            Element re = DocumentHelper.createElement("xml");
            Document doc = DocumentHelper.createDocument(re);
            re.addElement("FromUserName").addText(document.getRootElement().elementText("ToUserName"));
            re.addElement("ToUserName").addText(fromUserName);
            re.addElement("CreateTime").addText(new Date().getTime()/1000+"");
            RemexApplication.publishEvent(new WeChatEvent("wechat", curWeChatApp.key(),curWeChatApp.getConfig().getAppid(),curWeChatApp.getConfig().getAgentid(),msgType, fromUserName,toUserName,createTime, event, eventKey, ticket, document));
            //text  image  voice video shortvideo location link event
            switch (msgType) {
//                case "text": //文本，用户发的信息
//                    break;
                case "event":  //用户的事件
                    String openId = fromUserName;

                    switch (event) {
                        case "subscribe": //关注事件
                            String[] eventKeyParts = null;
                            RemexApplication.publishEvent(new WeChatSubscribeEvent("subscribe", curWeChatApp.key(),curWeChatApp.getConfig().getAppid(),curWeChatApp.getConfig().getAgentid(), openId, !Judgment.nullOrBlank(eventKey) && (eventKeyParts = eventKey.split("_")).length > 1 ? eventKeyParts[1] : null));
                            re.addElement("MsgType").addText("text");
                            re.addElement("Content").addText(Judgment.nullOrBlank(curWeChatApp.getConfig().getSubscribeMsg())?"欢迎您关注!":curWeChatApp.getConfig().getSubscribeMsg());
                            resXml = doc.asXML();
                            break;
                        case "SCAN": //扫码事件，已经关注后，再次扫码事件
                            RemexApplication.publishEvent(new WeChatScanEvent("SCAN", curWeChatApp.key(),curWeChatApp.getConfig().getAppid(),curWeChatApp.getConfig().getAgentid(), openId, eventKey, ticket));
                            break;
                        case "unsubscribe": //取消关注事件
                            RemexApplication.publishEvent(new WeChatUnsubscribeEvent("unsubscribe", curWeChatApp.key(),curWeChatApp.getConfig().getAppid(),curWeChatApp.getConfig().getAgentid(), openId, eventKey));
                            break;
                        case "TEMPLATESENDJOBFINISH": //模板发送成功
                            break;
                        case "click": // 点击菜单拉取消息事件
                        case "CLICK": // 点击菜单拉取消息事件
                            RemexApplication.publishEvent(new WeChatClickEvent("CLICK", curWeChatApp.key(),curWeChatApp.getConfig().getAppid(),curWeChatApp.getConfig().getAgentid(), fromUserName,  eventKey));
                            break;
                        case "LOCATION":
                            String latitude = document.getRootElement().elementText("Latitude");//地理位置纬度
                            String longitude = document.getRootElement().elementText("Longitude");//地理位置经度
                            String precision = document.getRootElement().elementText("Precision");//地理位置精度
                            RemexApplication.publishEvent(new WeChatLocationEvent("LOCATION", curWeChatApp.key(),curWeChatApp.getConfig().getAppid()
                                    ,curWeChatApp.getConfig().getAgentid(), fromUserName,toUserName, eventKey,latitude,longitude,precision,createTime));
                            break;
                    }
                    this.saveXMLFile(reqXml, msgType, event, fromUserName);
                    break;
                case "text": //文本，用户发的信息
                    String content = document.getRootElement().elementText("Content");
                    String msgId = document.getRootElement().elementText("MsgId");
                    RemexApplication.publishEvent(new WeChatTextEvent("wechatTextEvent", curWeChatApp.key(),curWeChatApp.getConfig().getAppid(),curWeChatApp.getConfig().getAgentid(),fromUserName,content,Long.valueOf(createTime),msgId));
                case "image":
                    String imgurl = document.getRootElement().elementText("PicUrl");
                    String msgIds = document.getRootElement().elementText("MsgId");
                    String mediaId = document.getRootElement().elementText("MediaId");
                    RemexApplication.publishEvent(new WeChatImageEvent("weChatImageEvent", curWeChatApp.key(),curWeChatApp.getConfig().getAppid(),curWeChatApp.getConfig().getAgentid(),fromUserName,imgurl,Long.valueOf(createTime),msgIds,mediaId));

                default:
                    re.addElement("MsgType").addText("transfer_customer_service");
                    resXml = doc.asXML();
                    break;
            }

        } catch (DocumentException e) {
            RemexConstants.logger.error("微信->XML 解析错误！！{}", e);
        }

        RemexConstants.logger.info("微信->响应微信信息：{}" , resXml);
        return resXml;
    }
    private boolean saveXMLFile(String xml, String type, String evenType, String fileName) {
        final String curSystem = System.getProperty("os.name");
        String baseXmlFilePath = null;
        if (curSystem.toUpperCase().startsWith("WIN")) {
//			findXMLFilePathSQL += "'ErrorXmlFileSavePath'";
            baseXmlFilePath = WeChat.WIN_PATH;
        } else if (curSystem.toUpperCase().startsWith("LINUX")) {
//			findXMLFilePathSQL += "'LinuxErrorXmlFileSavePath'";
            baseXmlFilePath = WeChat.LINUX_PATH;
        }
        Assert.notNull(baseXmlFilePath,  ServiceCode.ERROR,"文件路径不能为空！");
        LocalDateTime localDateTime = LocalDateTime.now().withNano(0);
        DateTimeFormatter f = DateTimeFormatter.ofPattern("HHmmss");
        StringBuilder xmlFilePath = new StringBuilder(baseXmlFilePath + File.separator + type + File.separator);
        if (!Judgment.nullOrBlank(evenType)) {
            xmlFilePath.append(evenType).append(File.separator);
        }
        xmlFilePath.append(localDateTime.toLocalDate().toString()).append(File.separator).append(localDateTime.toLocalTime().format(f)).append("-").append(fileName).append(".xml");
        FileHelper.saveFileContent(xmlFilePath.toString(), xml);
        return true;
    }

    @BusinessService(bodyParamName = "xml", needAuth = false)
    public BsRvo wechatPayReceiver(String xml) {
        RemexConstants.logger.info("收到微信支付通知：{}" , xml);
        PayNotify weChatPayNotifyOuter = new PayNotify();
        String respXmlPath = "RESPONSE_ERROR_"+String.valueOf(System.currentTimeMillis());
        if (Judgment.nullOrBlank(xml)) {
            weChatPayNotifyOuter.setReturn_code("FAIL ");
            weChatPayNotifyOuter.setReturn_msg("接收信息为空");
            RemexConstants.logger.info("收到微信支付通知消息为EMPTY");
        }else{
            PayNotify payNotify = XmlHelper.unmarshall(PayNotify.class, xml);
            if (!Judgment.nullOrBlank(payNotify)) {
                Map<String, String> map = MapHelper.objectToFlat(payNotify);
                if (Signature.validateSign(map, WeChat.getWeChatApp().getConfig().getKey())) {
                    this.saveXMLFile(xml, "PAY", payNotify.getOut_trade_no());
                    respXmlPath = payNotify.getOut_trade_no() + "response";
                    try {
                        RemexApplication.publishEvent(new WeChatPayNotifyEvent("WeChatPayNotifyEvent", payNotify));
                        weChatPayNotifyOuter.setReturn_code("SUCCESS");
                    } catch (Throwable throwable) {
                        RemexConstants.logger.error("CET报名支付微信端回调时，系统处理出现异常。{}",throwable);
                        weChatPayNotifyOuter.setReturn_code("FAIL");
                        weChatPayNotifyOuter.setReturn_msg(throwable.toString());
                    }
                }else {
                    weChatPayNotifyOuter.setReturn_code("FAIL");
                    weChatPayNotifyOuter.setReturn_msg("签名失败");
                }
            }else {
                weChatPayNotifyOuter.setReturn_code("FAIL");
                weChatPayNotifyOuter.setReturn_msg("解析报文体为空");
            }
        }
        String respXml = XmlHelper.marshall(weChatPayNotifyOuter);
        this.saveXMLFile(respXml, "PAY", respXmlPath);
        return new BsRvo(ServiceCode.SUCCESS, "OK", respXml, RemexConstants.ResponseContentType.text, null);
    }


    private boolean saveXMLFile(String xml, String type, String fileName) {
        final String curSystem = System.getProperty("os.name");
        String baseXmlFilePath = null;
        if (curSystem.toUpperCase().startsWith("WIN")) {
//			findXMLFilePathSQL += "'ErrorXmlFileSavePath'";
            baseXmlFilePath = WeChat.WIN_PATH;
        } else if (curSystem.toUpperCase().startsWith("LINUX")) {
//			findXMLFilePathSQL += "'LinuxErrorXmlFileSavePath'";
            baseXmlFilePath = WeChat.LINUX_PATH;
        }
        Assert.notNull(baseXmlFilePath, ServiceCode.ERROR, "文件路径不能为空！");
        LocalDateTime localDateTime = LocalDateTime.now().withNano(0);
        DateTimeFormatter f = DateTimeFormatter.ofPattern("HHmmss");
        FileHelper.saveFileContent(baseXmlFilePath + File.separator + type + File.separator + localDateTime.toLocalDate().toString() + File.separator + fileName + ".xml", xml);
        return true;
    }
}
